package homework

import (
	"errors"
	"fmt"
	"reflect"
	"strings"
)

var errInvalidEntity = errors.New("invalid entity")

// InsertStmt 作业里面我们这个只是生成 SQL，所以在处理 sql.NullString 之类的接口
// 只需要判断有没有实现 driver.Valuer 就可以了
func InsertStmt(entity interface{}) (string, []interface{}, error) {

	// val := reflect.ValueOf(entity)
	// typ := val.Type()
	// 检测 entity 是否符合我们的要求
	// 我们只支持有限的几种输入

	// 使用 strings.Builder 来拼接 字符串
	// bd := strings.Builder{}

	// 构造 INSERT INTO XXX，XXX 是你的表名，这里我们直接用结构体名字

	// 遍历所有的字段，构造出来的是 INSERT INTO XXX(col1, col2, col3)
	// 在这个遍历的过程中，你就可以把参数构造出来
	// 如果你打算支持组合，那么这里你要深入解析每一个组合的结构体
	// 并且层层深入进去

	// 拼接 VALUES，达成 INSERT INTO XXX(col1, col2, col3) VALUES

	// 再一次遍历所有的字段，要拼接成 INSERT INTO XXX(col1, col2, col3) VALUES(?,?,?)
	// 注意，在第一次遍历的时候我们就已经拿到了参数的值，所以这里就是简单拼接 ?,?,?

	// return bd.String(), args, nil
	//panic("implement me")

	val := reflect.ValueOf(entity)
	typ := reflect.TypeOf(entity)

	if val.Kind() == reflect.Invalid {
		return "", nil, errInvalidEntity
	}

	if typ.Kind() == reflect.Pointer {
		typ = typ.Elem()
		val = val.Elem()
	}

	if typ.Kind() == reflect.Pointer {
		return "", nil, errInvalidEntity
	}

	if typ.Kind() == reflect.Struct {
		//fdNum := typ.NumField()
		//cnt := 0
		//for i := 0; i < fdNum; i++ {
		//	fd := typ.Field(i)
		//	if fd.IsExported() {
		//		dict[fd.Name] = val.Field(i).Interface()
		//		cnt++
		//	}
		//}
		//if cnt == 0 {
		//	return "", nil, errInvalidEntity
		//}

		dict := NewDict()

		err := insertStmt(dict, val)
		if err != nil {
			return "", nil, err
		}

		keys, vals := dict.Entries()
		fills := make([]string, 0, len(keys))
		cols := make([]string, 0, len(keys))

		for _, col := range keys {
			cols = append(cols, fmt.Sprintf("`%s`", col))
			fills = append(fills, "?")
		}

		columnStr := strings.Join(cols, ",")
		fillStr := strings.Join(fills, ",")

		str := fmt.Sprintf("INSERT INTO `%s`(%s) VALUES(%s);", typ.Name(), columnStr, fillStr)
		return str, vals, nil
	}

	return "", nil, nil
}

func insertStmt(dict *Dict, val reflect.Value) error {
	typ := val.Type()
	fdNum := typ.NumField()
	for i := 0; i < fdNum; i++ {
		fd := typ.Field(i)

		if fd.Anonymous && fd.Type.Kind() == reflect.Struct {
			err := insertStmt(dict, val.Field(i))
			if err != nil {
				return err
			}
			continue
		}

		if fd.IsExported() {
			dict.Put(fd.Name, val.Field(i).Interface())
		}
	}
	if dict.Len() == 0 {
		return errInvalidEntity
	}

	return nil
}

type Dict struct {
	dict map[string]any
	keys []string
}

func NewDict() *Dict {
	return &Dict{
		dict: map[string]any{},
		keys: make([]string, 0, 8),
	}
}

func (d *Dict) Put(key string, val any) {
	if _, ok := d.dict[key]; ok {
		return

	}
	d.keys = append(d.keys, key)
	d.dict[key] = val
}

func (d *Dict) Entries() ([]string, []any) {
	vals := make([]any, 0, len(d.keys))
	for _, key := range d.keys {
		vals = append(vals, d.dict[key])
	}
	return d.keys, vals
}

func (d *Dict) Len() int {
	return len(d.keys)
}
